package dbu.dbase.version3;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import dbu.dbase.DBaseHeader;
import dbu.dbase.DBaseStructure;
import dbu.dbase.DataTypes;
import dbu.jdbc.JdbcResultSetMetaData;

public class TableFileHeader extends DBaseStructure implements DBaseHeader {

	/** Valid dBASE III PLUS table file (03h without a memo .DBT file; 83h with a memo). */
	public static final byte VERSION = 3;
	public static final byte DBT = (byte) 0x80;

	/**
	 * Date of last update; in YYMMDD format.  
	 * Each byte contains the number as a binary.  YY is added to a base of 1900 decimal to determine the actual year. 
	 * Therefore, YY has possible values from 0x00-0xFF, which allows for a range from 1900-2155.
	 */
	private Date lastUpdate;

	/**
	 * Number of records in the table.
	 */
	private int numberOfRecords;

	/**
	 * Number of bytes in the header.
	 */
	private short numberOfBytesInTheHeader;

	/**
	 * Number of bytes in the record.
	 */
	private short numberOfBytesInTheRecord;

	/**
	 * the field descriptor array.
	 */
	private List<FieldDescriptor> fieldDescriptors;

	public TableFileHeader() {
		fieldDescriptors = new ArrayList<FieldDescriptor>();
	}

	/**
	 * Read  
	 * @param stream
	 * @throws IOException
	 */
	@Override
	protected int read(InputStream stream) throws IOException {
		/** 1 byte */
		//version = readByte(stream);
		/** 3 bytes */
		lastUpdate = readYYMMDD(stream);
		/** 32-bit number (Least significant byte first.) */
		numberOfRecords = readInt(stream);
		/** 16-bit number (Least significant byte first.) */
		numberOfBytesInTheHeader = readShort(stream);
		/** 16-bit number (Least significant byte first.) */
		numberOfBytesInTheRecord = readShort(stream);
		/** 3 bytes 	Reserved; filled with zeros. */
		readBuff(stream, 3);
		/** 13 bytes 	Reserved for dBASE III PLUS on a LAN. */
		readBuff(stream, 13);
		/** 4 bytes 	Reserved. */
		readBuff(stream, 4);
		/** 32 bytes each */
		while (true) {
			FieldDescriptor fieldDescriptor = new FieldDescriptor();
			if (32 != fieldDescriptor.read(stream)) {
				break;
			}
			fieldDescriptors.add(fieldDescriptor);
		}
		return 32 + fieldDescriptors.size() * 32 + 1;
	}

	/**
	 * Write 
	 * @param stream
	 * @throws IOException
	 */
	protected void write(OutputStream stream) throws IOException {
		/** 1 byte */
		stream.write(VERSION);
		/** 3 bytes */
		stream.write(getYYMMDD(lastUpdate));
		/** 32-bit number (Least significant byte first.) */
		stream.write(numberOfRecords);
		/** 16-bit number (Least significant byte first.) */
		stream.write(numberOfBytesInTheHeader);
		/** 16-bit number (Least significant byte first.) */
		stream.write(numberOfBytesInTheRecord);
		/** 3 bytes 	Reserved; filled with zeros. */
		stream.write(new byte[] {0, 0, 0});
		/** 13 bytes 	Reserved for dBASE III PLUS on a LAN. */
		stream.write(new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
		/** 4 bytes 	Reserved. */
		stream.write(new byte[] {0, 0, 0, 0});
		/** 32 bytes each */
		for (FieldDescriptor fieldDescriptor: fieldDescriptors) {
			fieldDescriptor.write(stream);
		}
		/** 1 byte 	0x0D stored as the Field Descriptor terminator. */
		stream.write(FieldDescriptor.TERMINATER);
	}

	@Override
	public int getNumberOfRecords() {
		return numberOfRecords;
	}

	@Override
	public short getNumberOfBytesInTheRecord() {
		return numberOfBytesInTheRecord;
	}

	@Override
	public int getNumOfFields() {
		return fieldDescriptors.size();
	}

	@Override
	public DataTypes getFieldType(int index) {
		return fieldDescriptors.get(index).getFieldType();
	}

	@Override
	public String getFieldName(int index) {
		return fieldDescriptors.get(index).getFieldName();
	}

	@Override
	public byte getFieldLength(int index) {
		return fieldDescriptors.get(index).getFieldLength();
	}

	@Override
	public byte getFieldDecimalCount(int index) {
		return fieldDescriptors.get(index).getFieldDecimalCount();
	}

	@Override
	public String toString() {
		StringBuffer result = new StringBuffer();
		for (int i = 0; i < getNumOfFields(); i++) {
			String name = getFieldName(i);
			if (name.length() > getFieldLength(i)) {
				name = name.substring(0, getFieldLength(i));
			} else {
				while (name.length() < getFieldLength(i)) {
					name += " ";
				}
			}
			result.append(name);
		}
		return result.toString();
	}

	@Override
	public JdbcResultSetMetaData getMeta() {
		StringBuffer line = new StringBuffer();
		line.append(JdbcResultSetMetaData.SEMICOL);
		for (FieldDescriptor field: fieldDescriptors) {
			if (line.length() > 1) {
				line.append(JdbcResultSetMetaData.COMMA);
			}
			line.append(field.getFieldName().toLowerCase());
		}
		JdbcResultSetMetaData meta = new JdbcResultSetMetaData(line.toString());
		return meta;
	}
}
